import { useState, useEffect, useRef } from "react";
import { Component } from "react";

// 1. 类组件生命周期演示
class ClassLifecycleDemo extends Component<
  { name: string },
  { count: number; message: string }
> {
  // 构造函数阶段
  constructor(props: { name: string }) {
    super(props);
    console.log("ClassDemo: 1. Constructor - 组件实例化");
    
    // 初始化状态
    this.state = {
      count: 0,
      message: "初始消息",
    };
  }
  
  // 挂载阶段 - 组件即将挂载（React 17后已弃用）
  UNSAFE_componentWillMount() {
    console.log("ClassDemo: 2. UNSAFE_componentWillMount - 组件即将挂载（已弃用）");
  }
  
  // 挂载阶段 - 组件已挂载
  componentDidMount() {
    console.log("ClassDemo: 4. componentDidMount - 组件已挂载到DOM");
    
    // 模拟数据加载
    setTimeout(() => {
      this.setState({ message: "数据加载完成" });
    }, 1000);
  }
  
  // 更新阶段 - 组件是否应该更新
  shouldComponentUpdate(nextProps: { name: string }, nextState: { count: number; message: string }) {
    console.log("ClassDemo: 5. shouldComponentUpdate - 决定组件是否更新");
    
    // 只有当count变化时才更新
    const shouldUpdate = this.state.count !== nextState.count || this.state.message !== nextState.message;
    return shouldUpdate;
  }
  
  // 更新阶段 - 组件即将更新（React 17后已弃用）
  UNSAFE_componentWillUpdate() {
    console.log("ClassDemo: 6. UNSAFE_componentWillUpdate - 组件即将更新（已弃用）");
  }
  
  // 更新阶段 - 组件已更新
  componentDidUpdate(prevProps: { name: string }, prevState: { count: number; message: string }) {
    console.log("ClassDemo: 8. componentDidUpdate - 组件已更新");
    console.log(`状态从 ${prevState.count} 变为 ${this.state.count}`);
  }
  
  // 卸载阶段 - 组件即将卸载
  componentWillUnmount() {
    console.log("ClassDemo: 9. componentWillUnmount - 组件即将卸载");
  }
  
  // 错误处理阶段
  static getDerivedStateFromError(error: Error) {
    console.log("ClassDemo: getDerivedStateFromError - 捕获到错误");
    return { hasError: true };
  }
  
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.log("ClassDemo: componentDidCatch - 处理错误");
    console.error(error, info);
  }
  
  // 渲染方法 - 必须实现
  render() {
    console.log("ClassDemo: 3/7. render - 渲染组件");
    
    return (
      <div className="lifecycle-demo">
        <h4>类组件生命周期演示</h4>
        <p>名称: {this.props.name}</p>
        <p>计数: {this.state.count}</p>
        <p>消息: {this.state.message}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          增加计数
        </button>
      </div>
    );
  }
}

// 2. 函数组件生命周期演示（使用Hooks）
const FunctionLifecycleDemo = ({ name }: { name: string }) => {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState("初始消息");
  const [show, setShow] = useState(true);
  
  // 模拟 Constructor + componentDidMount - 仅运行一次
  useEffect(() => {
    console.log("FunctionDemo: 相当于 componentDidMount - 组件已挂载");
    
    // 模拟数据加载
    setTimeout(() => {
      setMessage("数据加载完成");
    }, 1000);
    
    // 返回清理函数，相当于 componentWillUnmount
    return () => {
      console.log("FunctionDemo: 相当于 componentWillUnmount - 组件即将卸载");
    };
  }, []);
  
  // 模拟 componentDidUpdate - 当依赖项变化时运行
  useEffect(() => {
    if (count > 0) { // 跳过初始渲染
      console.log("FunctionDemo: 相当于 componentDidUpdate - count已更新");
      console.log(`状态从 ${count - 1} 变为 ${count}`);
    }
  }, [count]);
  
  // 另一个针对message的更新效应
  useEffect(() => {
    if (message !== "初始消息") { // 跳过初始渲染
      console.log("FunctionDemo: 相当于 componentDidUpdate - message已更新");
    }
  }, [message]);
  
  // 每次渲染都会运行（没有依赖数组）
  useEffect(() => {
    console.log("FunctionDemo: 每次渲染后都会运行");
  });
  
  // 使用useRef跟踪渲染次数
  const renderCount = useRef(0);
  console.log(`FunctionDemo: 渲染 #${++renderCount.current}`);
  
  return (
    <div className="lifecycle-demo">
      <h4>函数组件生命周期演示（Hooks）</h4>
      <p>名称: {name}</p>
      <p>计数: {count}</p>
      <p>消息: {message}</p>
      <button onClick={() => setCount(count + 1)}>
        增加计数
      </button>
      <button onClick={() => setShow(!show)} style={{ marginLeft: "10px" }}>
        {show ? "隐藏" : "显示"}子组件
      </button>
      
      {/* 条件渲染子组件演示挂载/卸载 */}
      {show && <ChildComponent />}
    </div>
  );
};

// 子组件，用于演示挂载/卸载
const ChildComponent = () => {
  useEffect(() => {
    console.log("ChildComponent: 已挂载");
    
    return () => {
      console.log("ChildComponent: 即将卸载");
    };
  }, []);
  
  return <div>子组件</div>;
};

// 主演示组件
const ReactLifecycleDemo = () => {
  const [showClass, setShowClass] = useState(true);
  const [showFunction, setShowFunction] = useState(true);
  const [key, setKey] = useState(1);
  
  // 迫使组件重新挂载
  const remountComponents = () => {
    setKey(prevKey => prevKey + 1);
  };
  
  return (
    <div className="demo-section">
      <h2>React 生命周期面试题讲解</h2>
      
      <div className="card">
        <h3>1. React 组件生命周期是什么?</h3>
        <p>
          React 组件生命周期是指组件从创建到销毁的整个过程。它可以分为三个主要阶段:
        </p>
        <ol>
          <li><strong>挂载阶段 (Mounting):</strong> 组件被创建并插入到 DOM 中</li>
          <li><strong>更新阶段 (Updating):</strong> 组件重新渲染以响应 props 或 state 的变化</li>
          <li><strong>卸载阶段 (Unmounting):</strong> 组件从 DOM 中移除</li>
        </ol>
        
        <div className="flex-container">
          <div className="flex-item">
            <button onClick={() => setShowClass(!showClass)}>
              {showClass ? "隐藏" : "显示"}类组件
            </button>
            {showClass && <ClassLifecycleDemo key={`class-${key}`} name="类组件示例" />}
          </div>
          
          <div className="flex-item">
            <button onClick={() => setShowFunction(!showFunction)}>
              {showFunction ? "隐藏" : "显示"}函数组件
            </button>
            {showFunction && <FunctionLifecycleDemo key={`func-${key}`} name="函数组件示例" />}
          </div>
        </div>
        
        <button onClick={remountComponents} style={{ marginTop: "15px" }}>
          重新挂载所有组件
        </button>
        <p><small>提示: 请查看控制台输出以观察生命周期方法的调用顺序</small></p>
      </div>
      
      <div className="card">
        <h3>2. 常见面试问题: 类组件生命周期方法顺序</h3>
        <p>
          <strong>挂载阶段:</strong>
        </p>
        <ol>
          <li><code>constructor()</code> - 初始化 state 和绑定方法</li>
          <li><code>static getDerivedStateFromProps()</code> - 从 props 派生 state</li>
          <li><code>render()</code> - 渲染组件</li>
          <li><code>componentDidMount()</code> - 组件已挂载到 DOM，可以进行副作用操作</li>
        </ol>
        
        <p>
          <strong>更新阶段:</strong>
        </p>
        <ol>
          <li><code>static getDerivedStateFromProps()</code> - 从新 props 派生 state</li>
          <li><code>shouldComponentUpdate()</code> - 决定组件是否应该更新</li>
          <li><code>render()</code> - 重新渲染组件</li>
          <li><code>getSnapshotBeforeUpdate()</code> - 在更新前获取 DOM 信息</li>
          <li><code>componentDidUpdate()</code> - 组件已更新，可以操作更新后的 DOM</li>
        </ol>
        
        <p>
          <strong>卸载阶段:</strong>
        </p>
        <ol>
          <li><code>componentWillUnmount()</code> - 组件即将卸载，进行清理工作</li>
        </ol>
        
        <p>
          <strong>错误处理:</strong>
        </p>
        <ol>
          <li><code>static getDerivedStateFromError()</code> - 从错误中派生 state</li>
          <li><code>componentDidCatch()</code> - 捕获子组件中的错误</li>
        </ol>
      </div>
      
      <div className="card">
        <h3>3. 常见面试问题: 函数组件中的生命周期等效实现</h3>
        <table>
          <thead>
            <tr>
              <th>类组件生命周期方法</th>
              <th>函数组件 Hook 等效</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td><code>constructor</code></td>
              <td>
                <code>useState</code> 初始化 <br />
                <code>useRef</code> 创建实例变量
              </td>
            </tr>
            <tr>
              <td><code>componentDidMount</code></td>
              <td><code>useEffect(() =&gt; {"{...}"}, [])</code></td>
            </tr>
            <tr>
              <td><code>componentDidUpdate</code></td>
              <td><code>useEffect(() =&gt; {"{...}"}, [dependencies])</code></td>
            </tr>
            <tr>
              <td><code>componentWillUnmount</code></td>
              <td><code>useEffect(() =&gt; {"{...}"} return () =&gt; {"{...}"}, [])</code></td>
            </tr>
            <tr>
              <td><code>shouldComponentUpdate</code></td>
              <td><code>React.memo</code> + <code>useMemo</code> / <code>useCallback</code></td>
            </tr>
            <tr>
              <td><code>getDerivedStateFromProps</code></td>
              <td><code>useState</code> + <code>useEffect</code> 组合</td>
            </tr>
            <tr>
              <td><code>getSnapshotBeforeUpdate</code><br/><code>componentDidCatch</code></td>
              <td>无直接等效，可使用类组件包装</td>
            </tr>
          </tbody>
        </table>
      </div>
      
      <div className="card">
        <h3>4. 常见面试问题: 在生命周期方法中应该做什么?</h3>
        <ul>
          <li>
            <strong>constructor:</strong>
            <ul>
              <li>初始化 state</li>
              <li>绑定事件处理方法</li>
              <li>不应该调用 setState 或者执行副作用</li>
            </ul>
          </li>
          
          <li>
            <strong>componentDidMount:</strong>
            <ul>
              <li>DOM 操作</li>
              <li>数据请求</li>
              <li>添加事件监听器或订阅</li>
            </ul>
          </li>
          
          <li>
            <strong>componentDidUpdate:</strong>
            <ul>
              <li>根据 props 或 state 变化进行DOM操作</li>
              <li>根据条件发送网络请求</li>
              <li>需要条件判断避免无限循环</li>
            </ul>
          </li>
          
          <li>
            <strong>componentWillUnmount:</strong>
            <ul>
              <li>清理定时器</li>
              <li>取消网络请求</li>
              <li>清理订阅和事件监听器</li>
            </ul>
          </li>
        </ul>
      </div>
      
      <div className="card">
        <h3>5. 常见面试问题: 过时的生命周期方法</h3>
        <p>
          以下生命周期方法已被标记为不安全，并在 React 17 中完全移除:
        </p>
        <ul>
          <li>
            <code>componentWillMount</code> - 使用 <code>componentDidMount</code> 代替
          </li>
          <li>
            <code>componentWillReceiveProps</code> - 使用 <code>static getDerivedStateFromProps</code> 代替
          </li>
          <li>
            <code>componentWillUpdate</code> - 使用 <code>getSnapshotBeforeUpdate</code> 代替
          </li>
        </ul>
        <p>
          这些方法被废弃的原因主要是与 React 的异步渲染机制不兼容，可能导致不可预测的行为。
        </p>
      </div>
      
      <div className="card">
        <h3>6. 常见面试问题: 使用 useEffect 时的常见陷阱</h3>
        <p>
          <strong>无限循环:</strong> 如果在 useEffect 中更新了它依赖的状态，可能导致无限循环。
        </p>
        <div className="code-example">
          <code>
            // 错误示例 - 无限循环
            useEffect(() =&gt; {"{"}
              setCount(count + 1); // 更新依赖项，导致effect重新运行
            {"}"}, [count]);
            
            // 正确示例
            useEffect(() =&gt; {"{"}
              // 一次性操作，或者添加条件判断
              if (someCondition) {"{"}
                setCount(count + 1);
              {"}"}
            {"}"}, [count, someCondition]);
          </code>
        </div>
        
        <p>
          <strong>闭包陷阱:</strong> useEffect 中使用的函数会捕获渲染时的值。
        </p>
        <div className="code-example">
          <code>
            // 可能的问题
            useEffect(() =&gt; {"{"}
              const timer = setInterval(() =&gt; {"{"}
                setCount(count + 1); // 闭包中捕获的是旧的count值
              {"}"}, 1000);
              return () =&gt; clearInterval(timer);
            {"}"}, []);
            
            // 解决方案 - 使用函数式更新
            useEffect(() =&gt; {"{"}
              const timer = setInterval(() =&gt; {"{"}
                setCount(prevCount =&gt; prevCount + 1); // 使用最新状态
              {"}"}, 1000);
              return () =&gt; clearInterval(timer);
            {"}"}, []);
          </code>
        </div>
      </div>
      
      <div className="card">
        <h3>7. React 生命周期面试核心要点</h3>
        <ul>
          <li>理解类组件生命周期方法及其调用顺序</li>
          <li>熟悉 React 16.3 后的生命周期变更和废弃方法原因</li>
          <li>掌握函数组件中使用 useEffect 模拟生命周期行为</li>
          <li>了解每个生命周期阶段适合执行的操作类型</li>
          <li>区分挂载更新，以及理解重新渲染与重新挂载的区别</li>
          <li>掌握清理函数的作用及使用方法</li>
          <li>知道生命周期方法或 useEffect 中的常见错误和解决方案</li>
        </ul>
      </div>
    </div>
  );
};

export default ReactLifecycleDemo; 